package tp.igl;

import java.util.ArrayList;

/**
 * <b> Reseau est la classe representant un Réseau informatique</b>
 * <p>
 *  Un reseau est caractérisé par :
 *   <ul>
 *      <li>Une adresse réseau.</li>
 *      <li>Une adresse de broadcast</li>
 *      <li>La premiere adresse </li>
 *      <li>La derniere adresse </li>
 *      <li>Le masque du reseau</p>
 *   </ul>
 * </p>
 * @author PNR KACIMI HACINI
 * @see Adresse
 * @see AdresseIP
 * @see Masque
 */
public class Reseau {
    
    private AdresseIP networkAdress;
    private AdresseIP broadcastAdress;
    private AdresseIP firstAdress;
    private AdresseIP lastAdress;
    private Masque networkMasque;
    
    /**
     * Constructeur Reseau
     * <p>
     * A la construction , l'adresse réseau et le masque seront initialisées,et il calcul
     * l'adresse de broadcast, la premiere et la derniere adresse.
     * </p>
     * @param ip L'adresse réseau
     * @param masque Le masque réseau
     * @see Masque
     * @see Adresse
     * @see AdresseIP
     */
    public Reseau(AdresseIP ip,Masque masque){
        networkAdress=ip.adresseReseau(masque);
        networkMasque=masque;
        broadcastAdress=ip.broadcast(masque);
        firstAdress=networkAdress.nextIP();
        lastAdress=broadcastAdress.previousIP();
        networkAdress=ip.adresseReseau(masque);
        broadcastAdress=ip.broadcast(masque);
        
    }
    
    /**
     * Permet de mettre à jour les informations du réseau en se basant sur la nouvelle AdresseIP et le nouveau Masque
     * @param ip    nouvelle Adresse IP
     * @param masque nouveau Masque
     */
    public void refresh(AdresseIP ip,Masque masque){
        
        networkAdress=ip.adresseReseau(masque);
        broadcastAdress=ip.broadcast(masque);
        firstAdress=networkAdress.nextIP();
        lastAdress=broadcastAdress.previousIP();
        networkAdress=ip.adresseReseau(masque);
        broadcastAdress=ip.broadcast(masque);
    }
    
    
    
    
    /**
     * Retourne l'adresse réseau.
     * @return the networkAdress
     */
    public AdresseIP getNetworkAdress() {
        return networkAdress;
    }

    /**
     * Met a jour l'adresse réseau.
     * @param networkAdress the networkAdress to set
     */
    public void setNetworkAdress(AdresseIP networkAdress) {
        this.networkAdress = networkAdress;
    }

    /**
     * Retourne l'adresse broadcast
     * @return the broadcastAdress
     */
    public AdresseIP getBroadcastAdress() {
        return broadcastAdress;
    }

    /**
     * Met a jour l'adresse broadcast.
     * @param broadcastAdress the broadcastAdress to set
     */
    public void setBroadcastAdress(AdresseIP broadcastAdress) {
        this.broadcastAdress = broadcastAdress;
    }

    /**
     * Retourne la premiere adresse de réseau.
     * @return the firstAdress
     */
    public AdresseIP getFirstAdress() {
        return firstAdress;
    }

    /**
     * Met a jour la premiere adresse.
     * @param firstAdress the firstAdress to set
     */
    public void setFirstAdress(AdresseIP firstAdress) {
        this.firstAdress = firstAdress;
    }

    /**
     * Retourne la dreniere adresse de reseau.
     * @return the lastAdress
     */
    public AdresseIP getLastAdress() {
        return lastAdress;
    }

    /**
     * Met a jour la derniere adresse de reseau.
     * @param lastAdress the lastAdress to set
     */
    public void setLastAdress(AdresseIP lastAdress) {
        this.lastAdress = lastAdress;
    }
    
    /**
     * retourne la plus petite puissance de 2 superieure au nombre nb
     * @param nb
     * @return  Integer
     */
    public static Integer arronderNombre(int nb){
        if(nb==1 || nb==0)
            return 1;
        else if(nb<0)
            return 0;
        else{
           int n=(int)Math.round(Math.log(nb)/Math.log(2));
           if(Math.pow(2, n)<nb)
               n++;
           return (int)Math.pow(2, n);           
        }

    }
    
    /**
     * Permet de generer une liste des des sous-réseaux générés par la méthode supernet
     * Le nombre d'elements de cette liste est le paramètre nbSubnet
     * @param nbSubnet  nombre de sous-réseaux qu'on veut créer
     * @return  une ArrayList de réseaux contenant les sous-réseaux résultants
     */
    public ArrayList<Reseau> getSubetworks(int nbSubnet){
        ArrayList<Reseau> list=new ArrayList<>();
        nbSubnet=arronderNombre(nbSubnet);
        NbrMagic nbMagic=getNetworkMasque().getNbMagic(nbSubnet);
        Masque masque= this.getNetworkMasque().getSubnetMasque(nbSubnet);
        list.add(new Reseau(networkAdress, masque));
        for(int i=1; i<nbSubnet; i++){
           list.add(new Reseau(list.get(i-1).networkAdress.nextSubNetwork(nbMagic), masque));
        }
        return list;
    }
    
   /**
    * Retourne le nombres maximal de sous-reseaux qu'on puisse le  crée à partir de ce reseau
    * @return le nombres maximal de sous-reseau
    */
    public int getNbrMaxSubNets(){
        return networkMasque.getNbrMaxSubNets();
    }
    
    /**
     *  création d'une chaine de caractères contenants les informations du reseau
     * @return  concaténation des différentes informations du réseau
     */
    @Override
    public String toString() {
        return "\nReseau{" + "networkAdress=" + networkAdress.toString(10) + ", networkMasque=" + getNetworkMasque().toString(10) + '}';
    }

    /**
     * Retourne le masque réseau.
     * @return the networkMasque
     */
    public Masque getNetworkMasque() {
        return networkMasque;
    }
    
    
}
